package cmich.cps.scheduler.gui;

import java.awt.Image;
import java.awt.Toolkit;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.logging.Logger;

import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JProgressBar;
import javax.swing.JTabbedPane;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.WindowConstants;
import javax.swing.border.EmptyBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.JScrollPane;
import javax.swing.JTable;

import cmich.cps.scheduler.SchedulingSystem;
import cmich.cps.scheduler.algorithms.Algorithm;
import cmich.cps.scheduler.algorithms.Hybrid;
import cmich.cps.scheduler.algorithms.KruskalBased;
import cmich.cps.scheduler.algorithms.SuperHybrid;
import cmich.cps.scheduler.data.Professor;
import cmich.cps.scheduler.data.Schedule;

/**
 * The Class Frame.
 * 
 * @author Jonathan Kissinger <kissi1j@cmich.edu>
 */
public class Frame extends JFrame {

	/** The Constant serialVersionUID. */
	private static final long serialVersionUID = 1L;

	/** The Constant LOGGER. */
	private final static Logger LOGGER = Logger.getLogger(Frame.class.getName());

	/** The Constant frame. */
	private static final Frame frame = new Frame();

	/** The content pane. */
	private JTabbedPane contentPane;

	/** The prompts. */
	private Prompts prompts = Prompts.getInstance();

	/** The course controller. */
	private CourseController courseController;

	/** The room controller. */
	private RoomController roomController;

	/** The professor controller. */
	private ProfessorController professorController;

	/** The schedule controller. */
	private ScheduleController scheduleController;

	/** The progress bar. */
	private JProgressBar progressBar;

	/** The master schedule controller. */
	private MasterScheduleController masterScheduleController;

	/** The progress bar dialog. */
	private ProgressBarDialog progressBarDialog = new ProgressBarDialog(frame);

	/** The algorithm. */
	private Algorithm algorithm = null;

	/** The scheduling system. */
	private SchedulingSystem system = SchedulingSystem.getInstance();

	/**
	 * Instantiates a new frame.
	 */
	private Frame() {
		createWindow();
	}

	/**
	 * Gets the single instance of Frame.
	 *
	 * @return single instance of Frame
	 */
	public static Frame getInstance() {
		return frame;
	}

	/**
	 * Creates the window.
	 */
	public void createWindow() {
		LOGGER.fine("Creating main gui window.");
		try {
			UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
		} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException e) {
			LOGGER.warning(e.toString());
			prompts.nonFatalError(e.toString());
		}
		setTitle("CMU Scheduler");
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setBounds(0, 0, 800, 600);
		contentPane = new JTabbedPane();
		contentPane.setBorder(new EmptyBorder(5, 5, 5, 0));
		setContentPane(contentPane);

		setVisible(true);
		toFront();
		setExtendedState(NORMAL);
		setLocationRelativeTo(null);

		Image image = Toolkit.getDefaultToolkit().getImage(getClass().getClassLoader().getResource("logo.png"));
		setIconImage(image);
	}

	/**
	 * Populates the window.
	 */
	public void populateWindow() {
		LOGGER.fine("Creating course tab.");
		courseController = new CourseController(createTable("Courses"));
		LOGGER.fine("Populating course tab.");
		courseController.populate();
		LOGGER.fine("Adding course tab functionality.");
		courseController.addFunctionality();

		LOGGER.fine("Creating room tab.");
		roomController = new RoomController(createTable("Rooms"));
		LOGGER.fine("Populating room tab.");
		roomController.populate();
		LOGGER.fine("Adding room tab functionality.");
		roomController.addFunctionality();

		LOGGER.fine("Creating professor tab.");
		professorController = new ProfessorController(createTable("Professors"));
		LOGGER.fine("Populating professor tab.");
		professorController.populate();
		LOGGER.fine("Adding professor tab functionality.");
		professorController.addFunctionality();

		LOGGER.fine("Creating master schedule tab.");
		masterScheduleController = new MasterScheduleController(createTable("Manually assign courses"));
		LOGGER.fine("Populating master schedule tab.");
		masterScheduleController.populate();
		LOGGER.fine("Adding master schedule functionality.");
		masterScheduleController.addFunctionality();

		LOGGER.fine("Creating schedule tab.");
		scheduleController = new ScheduleController(createTable("Generated schedules"));
		LOGGER.fine("Populating schedule tab.");
		scheduleController.populate();
		LOGGER.fine("Adding schedule tab functionality.");
		scheduleController.addFunctionality();

		contentPane.addChangeListener(new ChangeListener() {
			public void stateChanged(ChangeEvent evt) {
				int tabIndex = contentPane.getSelectedIndex();
				if(tabIndex==0) {
					courseController.populate();
				}
				if(tabIndex==2) {
					professorController.populate();
				}
			}
		});
	}

	/**
	 * Edits the professor preferences.
	 *
	 * @param professor the professor
	 */
	public void editProfessorPreferences(Professor professor) {
		String title = professor.getName();
		int index = contentPane.indexOfTab(title);
		if(index==-1) {
			PreferenceController preferenceController = new PreferenceController(createTable(title), professor);
			preferenceController.populate();
			preferenceController.addFunctionality();
			index = contentPane.indexOfTab(title);
		}
		contentPane.setSelectedIndex(index);
	}

	/**
	 * View schedule.
	 *
	 * @param schedule the schedule
	 */
	public void viewSchedule(Schedule schedule) {
		String title = "Schedule - " + schedule.getId();
		int index = contentPane.indexOfTab(title);
		if(index==-1) {
			AssignedCourseController assignedCourseController = new AssignedCourseController(createTable(title), schedule);
			assignedCourseController.populate();
			assignedCourseController.addFunctionality();
			index = contentPane.indexOfTab(title);
		}
		contentPane.setSelectedIndex(index);
	}  

	/**
	 * Creates the table.
	 *
	 * @param title the title
	 * @return the table
	 */
	public JTable createTable(String title) {
		JScrollPane scrollPane = new JScrollPane();
		JTable table = new JTable();
		scrollPane.add(table);
		table.getParent();
		contentPane.addTab(title, null, scrollPane, null);
		scrollPane.setViewportView(table);
		return table;
	}

	/**
	 * Close tab.
	 *
	 * @param title the title of the tab
	 */
	public void closeTab(String title) {
		int index = contentPane.indexOfTab(title);
		if(index!=-1) {
			contentPane.remove(index);
		}
		if(title.toLowerCase().contains("schedule")) {
			contentPane.setSelectedIndex(4);
		}
		else {
			contentPane.setSelectedIndex(2);
		}
	}

	/**
	 * Start kruskal based.
	 */
	public void startKruskalBased() {
		algorithm = new KruskalBased();
		executeAlgorithm();
	}

	/**
	 * Start hybrid.
	 */
	public void startHybrid() {
		algorithm = new Hybrid();
		executeAlgorithm();
	}

	/**
	 * Start super hybrid.
	 */
	public void startSuperHybrid() {
		algorithm = new SuperHybrid();
		executeAlgorithm();
	}

	/**
	 * Execute the algorithm.
	 */
	private void executeAlgorithm() {
		algorithm.addPropertyChangeListener(new PropertyChangeListener() {
			@Override
			public void propertyChange(PropertyChangeEvent evt) {
				if ("progress".equals((evt.getPropertyName())) && !algorithm.isDone()) {
					int progress = (Integer) evt.getNewValue();
					progressBar.setValue(progress);
					progressBar.setString(progress + "%");
				}
			}
		});
		algorithm.execute();
		progressBar.setValue(0);
		progressBar.setString("0%");
		progressBarDialog.setVisible(true);
	}

	/**
	 * Adds the schedule.
	 *
	 * @param schedule the schedule
	 */
	public void addSchedule(Schedule schedule) {
		system.addSchedule(schedule);
		scheduleController.populate();
	}

	/**
	 * @see SchedulingSystem#importToMasterSchedule(int)
	 * @param index of the schedule to modify
	 */
	public void modifySchedule(int index) {
		if(system.importToMasterSchedule(index)) {
			masterScheduleController.populate();
			contentPane.setSelectedIndex(3);
		}
	}

	/**
	 * Used by scheduling algorithms, to display a progress bar that
	 * is updated to show the percentage progress of that algorithm.
	 *
	 * @author Jonathan Kissinger <kissi1j@cmich.edu>
	 */
	private class ProgressBarDialog extends JDialog {

		/** The Constant serialVersionUID. */
		private static final long serialVersionUID = 1L;

		/**
		 * Instantiates a new progress bar dialog.
		 *
		 * @param parent the parent {@link JFrame}
		 */
		public ProgressBarDialog(JFrame parent) {
			super(parent, true);
			progressBar = new JProgressBar(0, 100);
			progressBar.setStringPainted(true);
			this.add(progressBar);
			pack();
			setResizable(false);
			setLocationRelativeTo(parent);
			setVisible(false);
			setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
			setTitle("Progress");
			addWindowListener(new WindowAdapter() {
				@Override
				public void windowClosing(WindowEvent e) {
					if(!algorithm.isDone()) {
						algorithm.progressBarClosed();
					}
				}
			});

		}
	}

	/**
	 * Kill the progress bar.
	 */
	public void killProgressBar() {
		progressBarDialog.dispose();	
	}
}